﻿using WinFormsApp1.bo;
using WinFormsApp1.outputBo;
using WinFormsApp1.utils;

namespace WinFormsApp1.utils;

/**
 * 核心计算方法执行类
 */
// public class CacuResult
// {
//     public static void calculateActualRate(List<TPSTR04> TPSTR04List, List<TPMOUTR02> TPMOUTR02List,
//         List<TMMTR00> TMMTR00List,
//         List<ActualCutResult> ActualCutResultList, ref List<RateOfRecord> RateOfRecordList,
//         ref List<RateOfPlan> RateOfPlanList, ref List<RateOfAll> RateOfAllList)
//     {
//     }
// }
class CalculateActualRate
{
    public const double o_residue = 0.02; //定义最优棒材长度中超过标准坯长长度的范围
    public const double delta = 0.05; //定义非定尺合同管坯计算步长
    public const double l2max_0 = 4.5; //定义L2管坯最大长度                  
    public const double l2min_0 = 1.6; //定义L2管坯最小长度                   
    public const double pmax_0 = 11; //定义穿孔最大长度           
    public const double pmin_0 = 5.5; //定义穿孔最小长度           
    public const double sizemax_0 = 14; //定义空减最大长度                 
    public const double sizemin_0 = 5.5; //定义空减最小长度                   
    public const double rollmax_0 = 33; //定义连轧最大长度          
    public const double rollmin_0 = 8; //定义连轧最小长度           
    public const double srmmax_0 = 125; //定义张减最大长度                 
    public const double srmmin_0 = 8; //定义张减最小长度                   
    public const double l3max_0 = 21; //定义锯切长度L3最大长度          
    public const double l3min_0 = 7.6; //定义锯切长度L3最小长度

    public class StoreActualResult
    {
        public string RL_NO;
        public List<ActualCutResult> subActualCutResultList;

        public StoreActualResult(string rl_no, List<ActualCutResult> list)
        {
            RL_NO = rl_no;
            subActualCutResultList = list;
        }
    }

    // public CalculateActualRate(){
    //     List<Plan> planList = new List<Plan>();
    // }
    //主函数
    public void calculateActualRate(List<TPSTR04> TPSTR04List, List<TPMOUTR02> TPMOUTR02List, List<TMMTR00> TMMTR00List,
        List<ActualCutResult> ActualCutResultList, ref List<RateOfRecord> RateOfRecordList,
        ref List<RateOfPlan> RateOfPlanList, ref List<RateOfAll> RateOfAllList)
    {
        //投料实绩按照轧批号分组处理
        List<StoreActualResult> storeActualResultList = ActualCutResultList.GroupBy(x => x.RL_NO)
            .Select(x => new StoreActualResult(x.Key, x.ToList())).ToList();
        foreach (StoreActualResult curStore in storeActualResultList)
        {
            Plan curPlan = new Plan();
            List<RateOfRecord> subRateOfRecordList = new List<RateOfRecord>();
            List<TPSTR04> subTPSTR04List = TPSTR04List.FindAll(x => x.RL_NO == curStore.RL_NO); //返回所有为此轧批号的计划，可能不止一个
            TPMOUTR02 curRoll = TPMOUTR02List.Find(x => x.RL_NO == curStore.RL_NO);

            if (subTPSTR04List.Count == 0 || curRoll.RL_NO == "" || subTPSTR04List[0].LEN_FROM == 0 ||
                subTPSTR04List[0].LEN_TO == 0) //没有对应的有效计划或轧批
            {
                string remark = "";
                if (subTPSTR04List.Count == 0)
                {
                    remark += "无对应计划。";
                }
                else if (subTPSTR04List[0].LEN_FROM == 0 || subTPSTR04List[0].LEN_TO == 0)
                {
                    remark += "对应计划无效，交货长度起止值为0。";
                }

                if (curRoll.RL_NO == "")
                {
                    remark += "无对应轧批。";
                }

                foreach (ActualCutResult curResult in curStore.subActualCutResultList)
                {
                    RateOfRecord rateOfRecord = new RateOfRecord();
                    CutDetail curDetail = new CutDetail();
                    getRateOfRecord(curResult, remark, ref rateOfRecord); //需要判断是否有对应的计划或者轧批，备注plan为空
                    subRateOfRecordList.Add(rateOfRecord);
                }
                RateOfRecordList.AddRange(subRateOfRecordList);
                //统计此轧批的收得率
                RateOfPlanList.Add(statRecordToPlan(subRateOfRecordList));
                continue;
            }
            //else
            //此轧批对应的计划类：计划+工序长度要求+坯长范围+可行切割子模式列表（可为空）

            addPlanInfo(curPlan, subTPSTR04List);
            calculateLenLimit(curPlan, curRoll);
            addRollInfoAndModify(curPlan, curRoll);
            calculateFeasCutMode(curPlan);

            //计算每条投料记录的切割细节
            foreach (ActualCutResult curResult in curStore.subActualCutResultList)
            {
                CutDetail curDetail = new CutDetail();
                getCutInfo(curPlan, curResult, curDetail); //确定对应的切割模式。
                calculateCutDetails(curPlan, curDetail); //确定相应的切割子模式和收得率情况
                RateOfRecord rateOfRecord = new RateOfRecord();
                getRateOfRecord(curResult, curPlan, curRoll, curDetail, ref rateOfRecord);
                subRateOfRecordList.Add(rateOfRecord);
            }

            RateOfRecordList.AddRange(subRateOfRecordList);
            //统计此轧批的收得率
            RateOfPlanList.Add(statRecordToPlan(subRateOfRecordList));
        }

        //统计整体的收得率。
        RateOfAllList.Add(statAllPlan(RateOfPlanList));
    }

    /*
        将投料记录按照轧批号排序，根据轧批号，找到相应的计划和轧制信息，计算List<Plan>。
    */
    //获得计划信息
    public static void addPlanInfo(Plan curPlan, List<TPSTR04> subTPSTR04List)
    {
        if (subTPSTR04List.Count > 0)
        {
            TPSTR04 tmp = subTPSTR04List[0];
            curPlan.PLAN_DATE_FROM = tmp.PLAN_DATE_FROM;
            curPlan.PLAN_EXEC_SEQ_NO = tmp.PLAN_EXEC_SEQ_NO;
            curPlan.RL_NO = tmp.MTRL_NO;
            curPlan.MTRL_NO = tmp.MTRL_NO;
            curPlan.BILLET_LEN = tmp.BILLET_LEN;
            curPlan.BILLET_DIA = tmp.BILLET_DIA;
            curPlan.LEN_FROM = tmp.LEN_FROM;
            curPlan.LEN_TO = tmp.LEN_TO;
            curPlan.REQR_ROLL_TUBE = 0;
            curPlan.REQR_ROLL_WT = 0;

            //可能不止一个计划，需要增加计划根数等。
            for (int i = 0; i < subTPSTR04List.Count; i++)
            {
                curPlan.PLAN_SEQ_NO += String.Format("{0},", subTPSTR04List[i].PLAN_SEQ_NO.ToString());
                curPlan.REQR_ROLL_TUBE += subTPSTR04List[i].REQR_ROLL_TUBE;
                curPlan.REQR_ROLL_WT += subTPSTR04List[i].REQR_ROLL_WT;
            }

            curPlan.PLAN_SEQ_NO.Remove(curPlan.PLAN_SEQ_NO.Length - 1);

            if (curPlan.LEN_FROM == 0 || curPlan.LEN_TO == 0)
            {
                curPlan.REMARK += "交货长度起止值为0，无效计划。";
            }
        }
    }


    //计算工序长度限制范围
    public static void calculateLenLimit(Plan curPlan, TPMOUTR02 curRoll)
    {
        curPlan.l2max = l2max_0; //定义L2管坯最大长度                  
        curPlan.l2min = l2min_0; //定义L2管坯最小长度                   
        curPlan.pmax = pmax_0; //定义穿孔最大长度           
        curPlan.pmin = pmin_0; //定义穿孔最小长度           
        curPlan.sizemax = sizemax_0; //定义空减最大长度                 
        curPlan.sizemin = sizemin_0; //定义空减最小长度                   
        curPlan.rollmax = rollmax_0; //定义连轧最大长度          
        curPlan.rollmin = rollmin_0; //定义连轧最小长度           
        curPlan.srmmax = srmmax_0; //定义张减最大长度                 
        curPlan.srmmin = srmmin_0; //定义张减最小长度                   
        curPlan.l3max = l3max_0; //定义锯切长度L3最大长度          
        curPlan.l3min = l3min_0; //定义锯切长度L3最小长度

        curPlan.EXTEND1 = (double) ((decimal) curRoll.PIERC_LEN / (decimal) curRoll.BILLET_LEN); //定义延伸系数   穿孔长度/坯长度
        curPlan.EXTEND2 =
            (double) ((decimal) curRoll.SIZING_LEN / (decimal) curRoll.BILLET_LEN); //定义延伸系数   空减长度/坯长度
        curPlan.EXTEND3 =
            (double) ((decimal) curRoll.ROLLING_LEN / (decimal) curRoll.BILLET_LEN); //定义延伸系数   连轧长度/坯长度
        curPlan.EXTEND4 = (double) ((decimal) curRoll.SRM_LEN / (decimal) curRoll.BILLET_LEN); //定义延伸系数   张减管长度/坯长度
        
        //根据钢种的工序长度(有一些特殊情况)修改轧制计划
        if (curPlan.CNAME == "高压锅炉管") //高压锅炉管最大锯切长度
        {
            curPlan.l3max = 20.5;
        }

        if (curPlan.MTRL_NO == "2131" || curPlan.MTRL_NO == "2135" || curPlan.MTRL_NO == "2130" ||
            curPlan.MTRL_NO == "2134" || curPlan.MTRL_NO == "3838") //高强管最大锯切长度
        {
            curPlan.l3max = 12.5;
        }

        if (curPlan.MTRL_NO == "2123" || curPlan.MTRL_NO == "2145" || curPlan.MTRL_NO == "3118") //特殊普管材质放宽穿孔和连轧长度
        {
            curPlan.pmax = 11.15; //定义穿孔最大加长长度 
            curPlan.rollmax = 33.35; //定义连轧最大加长长度
        }

        //若计划值超过轧制表上下限，则扩大上下限。
        int roll_i = curPlan.roll_i;
        if (curPlan.l2max < curRoll.BILLET_LEN) //应该报错
        {
            curPlan.l2max = curRoll.BILLET_LEN;
        }

        if (curPlan.l2min > curRoll.BILLET_LEN)
        {
            curPlan.l2min = curRoll.BILLET_LEN;
        }

        if (curPlan.pmax < curRoll.PIERC_LEN) //穿孔
        {
            curPlan.pmax = curRoll.PIERC_LEN;
        }

        if (curPlan.pmin > curRoll.PIERC_LEN)
        {
            curPlan.pmin = curRoll.PIERC_LEN;
        }

        if (curPlan.sizemax < curRoll.SIZING_LEN)
        {
            curPlan.sizemax = curRoll.SIZING_LEN;
        }

        if (curPlan.sizemin > curRoll.SIZING_LEN)
        {
            curPlan.sizemin = curRoll.SIZING_LEN;
        }

        if (curPlan.rollmax < curRoll.ROLLING_LEN)
        {
            curPlan.rollmax = curRoll.ROLLING_LEN;
        }

        if (curPlan.rollmin > curRoll.ROLLING_LEN)
        {
            curPlan.rollmin = curRoll.ROLLING_LEN;
        }

        if (curPlan.srmmax < curRoll.SRM_LEN)
        {
            curPlan.srmmax = curRoll.SRM_LEN;
        }

        if (curPlan.srmmin > curRoll.SRM_LEN)
        {
            curPlan.srmmin = curRoll.SRM_LEN;
        }

        if (curPlan.l3max < curRoll.SAW_LEN)
        {
            curPlan.l3max = curRoll.SAW_LEN;
        }

        if (curPlan.l3min > curRoll.SAW_LEN)
        {
            curPlan.l3min = curRoll.SAW_LEN;
        }

        //根据工序长度计算管坯的可行范围。
        //轧制表中穿孔等工序需要考虑热膨胀。
        double[] lenmin = new double[5];
        double[] lenmax = new double[5];
        lenmin[0] = curPlan.l2min;
        lenmin[1] = curPlan.pmin / curPlan.EXTEND1;
        lenmin[2] = curPlan.sizemin / curPlan.EXTEND2;
        lenmin[3] = curPlan.rollmin / curPlan.EXTEND3;
        lenmin[4] = curPlan.srmmin / curPlan.EXTEND4;
        lenmax[0] = curPlan.l2max;
        lenmax[1] = curPlan.pmax / curPlan.EXTEND1;
        lenmax[2] = curPlan.sizemax / curPlan.EXTEND2;
        lenmax[3] = curPlan.rollmax / curPlan.EXTEND3;
        lenmax[4] = curPlan.srmmax / curPlan.EXTEND4;
        Array.Sort(lenmin);
        Array.Sort(lenmax);
        curPlan.billet_min = Math.Round(lenmin[4], 4, MidpointRounding.AwayFromZero); //最大的最小值
        curPlan.billet_max = Math.Round(lenmax[0], 4, MidpointRounding.AwayFromZero);
        ; //最小的最大值
    }


    //获得轧制信息以及计算轧制标准
    public static void addRollInfoAndModify(Plan curPlan, TPMOUTR02 curRoll)
    {
        if (curRoll.RL_NO != "")
        {
            // curPlan.EXTEND1 = (double) ((decimal) curRoll.PIERC_LEN / (decimal) curRoll.BILLET_LEN); //定义延伸系数   穿孔长度/坯长度
            // curPlan.EXTEND2 =
            //     (double) ((decimal) curRoll.SIZING_LEN / (decimal) curRoll.BILLET_LEN); //定义延伸系数   空减长度/坯长度
            // curPlan.EXTEND3 =
            //     (double) ((decimal) curRoll.ROLLING_LEN / (decimal) curRoll.BILLET_LEN); //定义延伸系数   连轧长度/坯长度
            // curPlan.EXTEND4 = (double) ((decimal) curRoll.SRM_LEN / (decimal) curRoll.BILLET_LEN); //定义延伸系数   张减管长度/坯长度
            curPlan.SP23 = (curRoll.SRM_LEN - curRoll.VALID_LEN);
            curPlan.VALID_LEN = curRoll.VALID_LEN;
            curPlan.SAW_LEN = curRoll.SAW_LEN;
            curPlan.SAW_NUM = (int) Math.Floor(curPlan.VALID_LEN / curPlan.SAW_LEN);
            curPlan.REMAIN_LEN = (curPlan.VALID_LEN - curPlan.SAW_NUM * curPlan.SAW_LEN); //实际的剩余长度  
            if (curRoll.MAKE_METHOD.Substring(0, 4) == "冷拔无缝" || curRoll.MAKE_METHOD.Substring(0, 4) == "冷轧无缝")
            {
                curPlan.LEN_FROM = curPlan.LEN_TO = curPlan.SAW_LEN;
                curPlan.L4ACT = curPlan.SAW_LEN;
                curPlan.SAW_N = 1;
                curPlan.SP440 = 0;
                curPlan.REMAIN_LEN = 0;
                curPlan.PRONUM = curPlan.SAW_NUM;
                curPlan.REQR_ROLL_LEN = curPlan.REQR_ROLL_TUBE * (curPlan.L4ACT * curPlan.PRONUM);
            }
            else //热拔无缝，根据交货长度来定。
            {
                //根据剩余长度来判断轧制表是否计算错误，需要调整
                //轧制表改为单倍尺后出错。修改锯切个数和有效长度及坯长等。
                //当结果中的计划坯长与轧制表中的计划坯长不同时，说明是轧制表出现此种错误。
                if ((curPlan.REMAIN_LEN / curPlan.SAW_LEN) >= 0.9)
                {
                    curPlan.REMARK += "剩余长度/锯切长度约为0.9，";
                    double billet_len =
                        Math.Round(((curPlan.SAW_NUM + 1) * curPlan.SAW_LEN + curPlan.SP23) / curPlan.EXTEND4, 4,
                            MidpointRounding.AwayFromZero);
                    if (billet_len <= curPlan.billet_max && billet_len >= curPlan.billet_min)
                    {
                        curPlan.SAW_NUM = curPlan.SAW_NUM + 1;
                        curPlan.VALID_LEN = curPlan.SAW_LEN * curPlan.SAW_NUM;
                        curPlan.REMAIN_LEN = 0;
                        curPlan.BILLET_LEN = billet_len; //修改计划坯长。
                        curPlan.REMARK += "计划坯长已修改成" + curPlan.BILLET_LEN + "。";
                    }
                    else
                    {
                        curPlan.REMARK += "计划坯长无法修改。";
                    }
                }

                //计算轧制标准
                if (curPlan.LEN_FROM >= curPlan.LEN_TO)
                {
                    //定尺合同
                    curPlan.SAW_N = (int) (curPlan.SAW_LEN / curPlan.LEN_FROM);
                    curPlan.SP440 = Math.Round(curPlan.SAW_LEN / curPlan.SAW_N - curPlan.LEN_FROM, 4,
                        MidpointRounding.AwayFromZero);
                    curPlan.L4ACT = curPlan.LEN_FROM + curPlan.SP440;
                    //当剩余长度不为0时，判断剩余长度是否满足要求。
                    if (curPlan.REMAIN_LEN != 0 && curPlan.REMAIN_LEN < curPlan.LEN_FROM)
                    {
                        curPlan.REMAIN_LEN = 0;
                    }
                    else if (curPlan.REMAIN_LEN != 0 && curPlan.REMAIN_LEN > curPlan.LEN_FROM &&
                             curPlan.REMAIN_LEN * curPlan.SAW_N < curPlan.SAW_LEN * (curPlan.SAW_N - 1))
                    {
                        curPlan.REMARK += "剩余长度可生产成品，但不满足定尺合同的锯切要求：2倍尺，剩余长度/锯切长度 > 0.5等。";
                        curPlan.REMAIN_LEN = 0;
                    }

                    curPlan.PRONUM = curPlan.SAW_N * curPlan.SAW_NUM + (int) (curPlan.REMAIN_LEN / curPlan.LEN_FROM);
                    curPlan.REQR_ROLL_LEN = curPlan.REQR_ROLL_TUBE * curPlan.L4ACT * curPlan.PRONUM;
                }
                else
                {
                    //非定尺合同
                    curPlan.SAW_N = (int) (curPlan.SAW_LEN / curPlan.LEN_TO);
                    if (curPlan.SAW_LEN / curPlan.SAW_N > curPlan.LEN_TO + 5) //当切成SAW_N份时，长度大于LEN_TO+5m，则多切一份。
                    {
                        curPlan.SAW_N = curPlan.SAW_N + 1;
                    }

                    curPlan.L4ACT = Math.Round(curPlan.SAW_LEN / curPlan.SAW_N, 4, MidpointRounding.AwayFromZero);
                    curPlan.SP440 = curPlan.L4ACT - curPlan.LEN_FROM;
                    if (curPlan.SP440 > 0.5)
                    {
                        curPlan.SP440 = 0.5;
                    }

                    //当剩余长度不为0时，判断剩余长度是否满足要求。
                    if (curPlan.REMAIN_LEN != 0 && curPlan.REMAIN_LEN < curPlan.LEN_FROM + curPlan.SP440)
                    {
                        curPlan.REMAIN_LEN = 0;
                    }

                    curPlan.PRONUM = curPlan.SAW_N * curPlan.SAW_NUM + (int) (curPlan.REMAIN_LEN / curPlan.LEN_FROM);
                    curPlan.REQR_ROLL_LEN = curPlan.REQR_ROLL_TUBE * curPlan.PRONUM * curPlan.L4ACT;
                    //计算交货长度的允许范围
                    curPlan.l4min = curPlan.LEN_FROM + curPlan.SP440;
                    if (curPlan.l4min * curPlan.SAW_N < curPlan.l3min)
                    {
                        curPlan.l4min = (double) ((decimal) curPlan.l3min / curPlan.SAW_N);
                    }

                    curPlan.l4max = curPlan.LEN_TO;
                    if (curPlan.l4max * curPlan.SAW_N < curPlan.SAW_LEN)
                    {
                        curPlan.l4max = (double) ((decimal) curPlan.SAW_LEN / (decimal) curPlan.SAW_N);
                    }

                    if (curPlan.l4max * curPlan.SAW_N > curPlan.l3max)
                    {
                        curPlan.l4max = (double) ((decimal) curPlan.l3max / curPlan.SAW_N);
                    }
                    //结束轧制标准
                }
            }

            curPlan.RATE = Math.Round(curPlan.PRONUM * curPlan.L4ACT / curPlan.BILLET_LEN / curPlan.EXTEND4, 6,
                MidpointRounding.AwayFromZero);
            ; //计划坯长的收得率
            curPlan.CNAME = curRoll.PROD_MID_CNAME; //定义品名分类
        }
    }

    //计算此轧批的可行切割子模式列表
    public static void calculateFeasCutMode(Plan curPlan)
    {
        if (curPlan.LEN_FROM >= curPlan.LEN_TO) //定尺合同
        {
            curPlan.feasBillet1List = new List<FeasBillet1>();

            //先将计划坯长的切割组合加入billet_list中。
            FeasBillet1 billet = new FeasBillet1(); //创建一个组合对象。
            billet.pronum = curPlan.PRONUM;
            billet.saw_num = curPlan.SAW_NUM;
            billet.remain_saw_n = curPlan.PRONUM % curPlan.SAW_N;
            billet.valid_len = curPlan.VALID_LEN;
            billet.billet_len = curPlan.BILLET_LEN;
            curPlan.feasBillet1List.Add(billet);

            //求出最小坯长能够生产的成品根数。
            double valid = curPlan.billet_min * curPlan.EXTEND4 - curPlan.SP23;
            int minpronum = (int) ((decimal) valid / (decimal) (curPlan.L4ACT));
            if (minpronum == 0)
            {
                minpronum = 1;
            }

            //遍历可能的成品根数。
            for (int pronum = curPlan.PRONUM - 1; pronum >= minpronum; pronum--)
            {
                int saw_num = (pronum / curPlan.SAW_N);
                int remain_pronum = pronum - saw_num * curPlan.SAW_N; //剩余长度可以生产的成品根数

                if (remain_pronum != 0 && remain_pronum < (curPlan.SAW_N - 1)) //剩余长度不满足定尺要求
                {
                    continue;
                }
                else if (remain_pronum != 0 && remain_pronum * (curPlan.L4ACT) < curPlan.l3min) //剩余长度小于最小锯切长度
                {
                    FeasBillet1 billet2 = new FeasBillet1(); //创建一个组合对象。
                    billet2.pronum = pronum;
                    billet2.saw_num = saw_num;
                    billet2.remain_saw_n = remain_pronum;
                    billet2.valid_len = (pronum - remain_pronum) * curPlan.L4ACT + curPlan.l3min;
                    billet2.billet_len =
                        Math.Round((double) ((decimal) (billet2.valid_len + curPlan.SP23) / (decimal) curPlan.EXTEND4),
                            4, MidpointRounding.AwayFromZero);
                    if (billet2.billet_len < curPlan.billet_min)
                    {
                        billet2.billet_len = curPlan.billet_min;
                    }

                    curPlan.feasBillet1List.Add(billet2);
                }
                else
                {
                    FeasBillet1 billet2 = new FeasBillet1(); //创建一个组合对象。
                    billet2.pronum = pronum;
                    billet2.saw_num = saw_num;
                    billet2.remain_saw_n = remain_pronum;
                    billet2.valid_len = pronum * curPlan.L4ACT;
                    billet2.billet_len =
                        Math.Round((double) ((decimal) (billet2.valid_len + curPlan.SP23) / (decimal) curPlan.EXTEND4),
                            4, MidpointRounding.AwayFromZero);
                    if (billet2.billet_len < curPlan.billet_min)
                    {
                        billet2.billet_len = curPlan.billet_min;
                    }

                    curPlan.feasBillet1List.Add(billet2);
                }
            }
        }
        else //非定尺合同
        {
            curPlan.feasBillet2List = new List<FeasBillet2>();

            double valid = curPlan.billet_min * curPlan.EXTEND4 - curPlan.SP23;
            int minpronum = (int) ((decimal) valid / (decimal) curPlan.LEN_TO);

            for (int pronum = curPlan.PRONUM; pronum >= minpronum; pronum--)
            {
                double billet_min =
                    Math.Round((double) ((decimal) (pronum * curPlan.l4min + curPlan.SP23) / (decimal) curPlan.EXTEND4),
                        4, MidpointRounding.AwayFromZero);
                double billet_max =
                    Math.Round((double) ((decimal) (pronum * curPlan.l4max + curPlan.SP23) / (decimal) curPlan.EXTEND4),
                        4, MidpointRounding.AwayFromZero);

                if (billet_min > curPlan.billet_max) //整个区间超出上限。
                {
                    continue;
                }
                else if (billet_max < curPlan.billet_min) //整个区间超出下限，以下限作为一个组合。
                {
                    FeasBillet2 billet = new FeasBillet2();
                    billet.billet_max = curPlan.billet_min;
                    billet.l4_max = curPlan.l4max;
                    billet.billet_min = curPlan.billet_min;
                    billet.l4_min = curPlan.l4max;

                    billet.pronum = pronum;
                    billet.saw_num = pronum / curPlan.SAW_N;
                    billet.remain_saw_n = pronum - billet.saw_num * curPlan.SAW_N;

                    curPlan.feasBillet2List.Add(billet);
                }
                else
                {
                    FeasBillet2 billet = new FeasBillet2(); //创建一个对象
                    billet.billet_min = billet_min;
                    billet.billet_max = billet_max;
                    billet.l4_min = curPlan.l4min;
                    billet.l4_max = curPlan.l4max;
                    if (billet_max > curPlan.billet_max) //一部分区间超出上限
                    {
                        billet.billet_max = curPlan.billet_max;
                        billet.l4_max = (double) ((decimal) (billet.billet_max * curPlan.EXTEND4 - curPlan.SP23) /
                                                  (decimal) pronum);
                    }

                    if (billet_min < curPlan.billet_min) //一部分区间超出下限
                    {
                        billet.billet_min = curPlan.billet_min; //下限不满足limit，所以要以limit为准。
                        billet.l4_min = (double) ((decimal) (billet.billet_min * curPlan.EXTEND4 - curPlan.SP23) /
                                                  (decimal) pronum);
                    }

                    billet.pronum = pronum;
                    billet.saw_num = pronum / curPlan.SAW_N;
                    billet.remain_saw_n = pronum - billet.saw_num * curPlan.SAW_N;

                    curPlan.feasBillet2List.Add(billet);
                }
            }
        }
    }


    /*
        全局静态方法：在切割细节类中，根据切割模式计算切割子模式和收得率。
    */
    public static void getCutInfo(Plan curPlan, ActualCutResult curResult, CutDetail curDetail)
    {
        curDetail.MAT_LEN = curResult.MAT_LEN;
        curDetail.FIX_FLAG = curResult.FIX_FLAG.ToString();
        curDetail.MAT_CUTNUM = curResult.MAT_TUBE;
        curDetail.CUT_LEN = curResult.CUT_LEN;
        curDetail.CUT_BILLET = (int) Math.Floor((decimal) curResult.MAT_LEN / (decimal) curResult.CUT_LEN);
        curDetail.REMAIN_LEN = curResult.REMAIN_LEN;
        curDetail.REMAIN_BILLET = (curResult.REMAIN_LEN > 0) ? 1 : 0;
        curDetail.SP_RUBBISH =
            Math.Round(
                curDetail.MAT_LEN - curDetail.CUT_LEN * curDetail.CUT_BILLET - curDetail.REMAIN_LEN -
                0.01 * (curDetail.REMAIN_BILLET + curDetail.CUT_BILLET), 4, MidpointRounding.AwayFromZero);
        if (curDetail.SP_RUBBISH > 0)
        {
            curDetail.SAWCUT12 = curDetail.REMAIN_BILLET + curDetail.CUT_BILLET;
        }
        else
        {
            curDetail.SAWCUT12 = curDetail.REMAIN_BILLET + curDetail.CUT_BILLET - 1;
        }

        //检查是否在范围内
        if (curDetail.CUT_LEN < curPlan.billet_min || curDetail.CUT_LEN > curPlan.billet_max ||
            curDetail.REMAIN_LEN < curPlan.billet_min || curDetail.REMAIN_LEN > curPlan.billet_max)
        {
            curPlan.REMARK += String.Format("投料切割的坯长超出限制。L2坯长下限为：{0:f3}；L2坯长下限为：{1:f3}。", curPlan.billet_min,
                curPlan.billet_max);
        }
    }


    public static void calculateCutDetails(Plan curPlan, CutDetail curDetail)
    {
        //1. ************正料坯长
        //若切割坯长是计划坯长，按计划坯长的切割子模式计算。
        if ((decimal) curDetail.CUT_LEN >= (decimal) curPlan.BILLET_LEN - (decimal) 0.01)
        {
            curDetail.CUT_VALID = curPlan.VALID_LEN;
            curDetail.A_SAW_LEN = curPlan.SAW_LEN;
            curDetail.A_SAW_NUM = curPlan.SAW_NUM;
            curDetail.A_REMAIN_SAW_N = curPlan.PRONUM - curPlan.SAW_NUM * curPlan.SAW_N;
            curDetail.L4ACT = curPlan.L4ACT;
            curDetail.A_NUML4 = curPlan.PRONUM;
        }
        else
        {
            if (curPlan.LEN_FROM >= curPlan.LEN_TO)
            {
                //定尺合同
                curDetail.A_SAW_LEN = curPlan.SAW_LEN;
                curDetail.L4ACT = curPlan.L4ACT;
                //检查最接近哪个可行切割子模式坯长
                foreach (FeasBillet1 feasBillet in curPlan.feasBillet1List)
                {
                    if (curDetail.CUT_LEN >= feasBillet.billet_len)
                    {
                        curDetail.CUT_VALID = curDetail.CUT_LEN * curPlan.EXTEND4 - curPlan.SP23;
                        curDetail.A_SAW_NUM = feasBillet.saw_num;
                        curDetail.A_REMAIN_SAW_N = feasBillet.remain_saw_n;
                        curDetail.A_NUML4 = feasBillet.pronum;
                        break;
                    }
                }
            }
            else
            {
                //非定尺合同

                //检查此坯长适合哪个锯切组合。
                foreach (FeasBillet2 feasBillet in curPlan.feasBillet2List)
                {
                    if (curDetail.CUT_LEN >= feasBillet.billet_min && curDetail.CUT_LEN <= feasBillet.billet_max)
                    {
                        curDetail.CUT_VALID = curDetail.CUT_LEN * curPlan.EXTEND4 - curPlan.SP23;
                        curDetail.A_SAW_NUM = feasBillet.saw_num;
                        curDetail.A_REMAIN_SAW_N = feasBillet.remain_saw_n;
                        curDetail.A_NUML4 = feasBillet.pronum;
                        curDetail.L4ACT = (double) ((decimal) curDetail.CUT_VALID / curDetail.A_NUML4);
                        curDetail.A_SAW_LEN = curDetail.L4ACT * curPlan.SAW_N;
                        break;
                    }
                }

                //坯长不符合上面要求，只能按照组合的上限生产。
                if (curDetail.A_NUML4 == 0)
                {
                    foreach (FeasBillet2 feasBillet in curPlan.feasBillet2List)
                    {
                        if (curDetail.CUT_LEN >= feasBillet.billet_max)
                        {
                            curDetail.CUT_VALID = curDetail.CUT_LEN * curPlan.EXTEND4 - curPlan.SP23;
                            curDetail.A_SAW_NUM = feasBillet.saw_num;
                            curDetail.A_REMAIN_SAW_N = feasBillet.remain_saw_n;
                            curDetail.A_NUML4 = feasBillet.pronum;
                            curDetail.L4ACT = feasBillet.l4_max;
                            curDetail.A_SAW_LEN = curDetail.L4ACT * curPlan.SAW_N;
                            break;
                        }
                    }
                }
            }
        }

        //根据剩余倍尺是否为0计算正料的剩余长度。
        if (curDetail.A_REMAIN_SAW_N != 0)
        {
            curDetail.A_REMAIN_LEN = Math.Round(curDetail.CUT_VALID - curDetail.A_SAW_NUM * curDetail.A_SAW_LEN, 4,
                MidpointRounding.AwayFromZero);
            curDetail.A_REPLACE = 0;
        }
        else
        {
            curDetail.A_REMAIN_LEN = 0;
            curDetail.A_REPLACE = Math.Round(curDetail.CUT_VALID - curDetail.A_SAW_NUM * curDetail.A_SAW_LEN, 4,
                MidpointRounding.AwayFromZero);
            //if (curDetail.A_REPLACE < 0 && curDetail.A_REPLACE > -0.001)
            //{
            //    curDetail.A_REPLACE = 0;
            //}
        }


        //2. **********************余料坯长
        if (curDetail.REMAIN_LEN != 0)
        {
            //若切割坯长是计划坯长，按计划坯长的切割子模式计算。
            if ((decimal) curDetail.REMAIN_LEN >= (decimal) curPlan.BILLET_LEN - (decimal) 0.01)
            {
                curDetail.REMAIN_VALID = curPlan.VALID_LEN;
                curDetail.SP_SAW_NUM = curPlan.SAW_NUM;
                curDetail.SP_REMAIN_SAW_N = curPlan.PRONUM - curPlan.SAW_NUM * curPlan.SAW_N;
                curDetail.SP_NUML4 = curPlan.PRONUM;
            }
            else
            {
                if (curPlan.LEN_FROM >= curPlan.LEN_TO)
                {
                    //定尺合同
                    //检查最接近哪个可行切割子模式坯长
                    foreach (FeasBillet1 feasBillet in curPlan.feasBillet1List)
                    {
                        if (curDetail.REMAIN_LEN >= feasBillet.billet_len)
                        {
                            curDetail.REMAIN_VALID = curDetail.REMAIN_LEN * curPlan.EXTEND4 - curPlan.SP23;
                            curDetail.SP_SAW_NUM = feasBillet.saw_num;
                            curDetail.SP_REMAIN_SAW_N = feasBillet.remain_saw_n;
                            curDetail.SP_NUML4 = feasBillet.pronum;
                            break;
                        }
                    }
                }
                else
                {
                    //非定尺合同
                    curDetail.REMAIN_VALID = curDetail.REMAIN_LEN * curPlan.EXTEND4 - curPlan.SP23;
                    curDetail.SP_SAW_NUM =
                        (int) Math.Floor((decimal) curDetail.REMAIN_VALID / (decimal) curDetail.A_SAW_LEN);
                    curDetail.SP_NUML4 = (int) Math.Floor((decimal) curDetail.REMAIN_VALID / (decimal) curDetail.L4ACT);
                    curDetail.SP_REMAIN_SAW_N = curDetail.SP_NUML4 - curDetail.SP_SAW_NUM * curPlan.SAW_N;
                }
            }

            //若余料的剩余长度还未被赋值，根据剩余倍尺是否为0计算正料的剩余长度。
            if (curDetail.SP_REMAIN_SAW_N != 0)
            {
                curDetail.SP_REMAIN_LEN =
                    Math.Round(curDetail.REMAIN_VALID - curDetail.SP_SAW_NUM * curDetail.A_SAW_LEN, 4,
                        MidpointRounding.AwayFromZero);
                curDetail.SP_REPLACE = 0;
            }
            else
            {
                curDetail.SP_REMAIN_LEN = 0;
                curDetail.SP_REPLACE = Math.Round(curDetail.REMAIN_VALID - curDetail.SP_SAW_NUM * curDetail.A_SAW_LEN,
                    4, MidpointRounding.AwayFromZero);
                //if (curDetail.SP_REPLACE < 0 && curDetail.SP_REPLACE > -0.01)
                //{
                //    curDetail.SP_REPLACE = 0;
                //}
            }
        }

        //3. ***************计算棒材指标
        //锯切刀数（主料+余料）
        if (curDetail.A_SAW_NUM != 0 && curDetail.A_REMAIN_LEN == 0 && curDetail.A_REPLACE == 0)
        {
            curDetail.SAWCUT23 = (curDetail.A_SAW_NUM - 1) * curDetail.CUT_BILLET;
        }
        else
        {
            curDetail.SAWCUT23 = curDetail.A_SAW_NUM * curDetail.CUT_BILLET;
        }

        if (curDetail.SP_SAW_NUM != 0 && curDetail.SP_REMAIN_LEN == 0 && curDetail.SP_REPLACE == 0)
        {
            curDetail.SAWCUT23 = curDetail.SAWCUT23 + curDetail.SP_SAW_NUM - 1;
        }
        else
        {
            curDetail.SAWCUT23 = curDetail.SAWCUT23 + curDetail.SP_SAW_NUM;
        }

        //成品根数
        curDetail.SIGMA_NUML4 = curDetail.CUT_BILLET * curDetail.A_NUML4 + curDetail.SP_NUML4;
        //棒材的有效成品长度
        if (curPlan.LEN_FROM >= curPlan.LEN_TO)
        {
            curDetail.TOTAL_LENL4 =
                Math.Round(curDetail.SIGMA_NUML4 * curDetail.L4ACT, 4, MidpointRounding.AwayFromZero);
        }
        else
        {
            curDetail.TOTAL_LENL4 =
                Math.Round(curDetail.SIGMA_NUML4 * curDetail.L4ACT, 4, MidpointRounding.AwayFromZero);
        }

        //收得率
        curDetail.RATE = curDetail.TOTAL_LENL4 / curPlan.EXTEND4 / curDetail.MAT_LEN;
        //坯长均值/计划坯长
        double len1 = (curDetail.CUT_LEN >= curPlan.BILLET_LEN) ? curPlan.BILLET_LEN : curDetail.CUT_LEN;
        double len2 = (curDetail.REMAIN_LEN >= curPlan.BILLET_LEN) ? curPlan.BILLET_LEN : curDetail.REMAIN_LEN;
        curDetail.AVRL2 = (curDetail.CUT_BILLET * len1 + curDetail.REMAIN_BILLET * len2) /
                          (curDetail.CUT_BILLET + curDetail.REMAIN_BILLET);
    }

    //针对无效计划或无对应计划/轧批的投料记录的收得率记录表，没有必要记录
    public static void getRateOfRecord(ActualCutResult curResult, string remark, ref RateOfRecord record)
    {
        record.CUT_DATE = curResult.CUT_DATE;
        record.CUT_TIME = curResult.CUT_TIME;
        record.PONO = curResult.PONO;
        record.STOCK_PLACE_NO = curResult.STOCK_PLACE_NO;
        record.FIX_FLAG = curResult.FIX_FLAG;
        record.MAT_LEN = curResult.MAT_LEN;
        record.MAT_TUBE = curResult.MAT_TUBE;
        record.CUT_WEIGHT = curResult.CUT_WEIGHT;
        record.CUT_LEN = curResult.CUT_LEN;
        record.CUT_BILLET = curResult.CUT_BILLET;
        record.REMAIN_LEN = curResult.REMAIN_LEN;
        record.REMAIN_BILLET = curResult.REMAIN_BILLET;
        record.REMARK = remark;
    }
    //if (curPlan.BILLET_LEN != 0)    //有对应计划
    //{
    //    record.MTRL_NO = curPlan.MTRL_NO;
    //    record.RL_NO = curPlan.RL_NO;
    //    record.PLAN_SEQ_NO = curPlan.PLAN_SEQ_NO;
    //    record.BILLET_DIA = curPlan.BILLET_DIA;
    //    record.BILLET_LEN = curPlan.BILLET_LEN;
    //    record.REQR_ROLL_TUBE = curPlan.REQR_ROLL_TUBE;
    //    record.REQR_ROLL_WT = curPlan.REQR_ROLL_WT;
    //    record.LEN_FROM = curPlan.LEN_FROM;
    //    record.LEN_TO = curPlan.LEN_TO;

    //}else if (curRoll.RL_NO == "")    //对应轧批
    //{
    //    record.MTRL_NO = curRoll.MTRL_NO;
    //    record.RL_NO = curRoll.RL_NO;
    //    record.BILLET_DIA = curRoll.BILLET_DIA;
    //    record.BILLET_LEN = curRoll.BILLET_LEN;
    //    record.REQR_ROLL_WT = curRoll.REQR_ROLL_WT;
    //}


    //根据curPlan和curDetail，以及投料记录赋值每条记录的收得率
    public static void getRateOfRecord(ActualCutResult curResult, Plan curPlan, TPMOUTR02 curRoll, CutDetail curDetail,
        ref RateOfRecord record)
    {
        record.MTRL_NO = curPlan.MTRL_NO;
        record.RL_NO = curPlan.RL_NO;
        record.PLAN_SEQ_NO = curPlan.PLAN_SEQ_NO;
        record.BILLET_DIA = curPlan.BILLET_DIA;
        record.BILLET_LEN = curPlan.BILLET_LEN;
        record.REQR_ROLL_TUBE = curPlan.REQR_ROLL_TUBE;
        record.REQR_ROLL_WT = curPlan.REQR_ROLL_WT;
        record.RATE = curPlan.RATE;
        record.LEN_FROM = curPlan.LEN_FROM;
        record.LEN_TO = curPlan.LEN_TO;
        //record.REQR_ROLL_LEN = curPlan.REQR_ROLL_LEN;

        record.CUT_DATE = curResult.CUT_DATE;
        record.CUT_TIME = curResult.CUT_TIME;
        record.PONO = curResult.PONO;
        record.STOCK_PLACE_NO = curResult.STOCK_PLACE_NO;
        record.FIX_FLAG = curResult.FIX_FLAG;
        record.MAT_LEN = curResult.MAT_LEN;
        record.MAT_TUBE = curResult.MAT_TUBE;
        record.CUT_WEIGHT = curResult.CUT_WEIGHT;
        record.CUT_LEN = curResult.CUT_LEN;
        record.CUT_BILLET = curResult.CUT_BILLET;
        record.REMAIN_LEN = curResult.REMAIN_LEN;
        record.REMAIN_BILLET = curResult.REMAIN_BILLET;

        record.ACTUAL_RATE = curDetail.RATE;
        record.ACTUAL_AVG_L2 = curDetail.AVRL2;
        record.COMP_RATE = curDetail.RATE / curPlan.RATE;
        record.COMP_AVG_L2 = curDetail.AVRL2 / curPlan.BILLET_LEN;
        record.COMP_WEIGHT = curResult.MAT_TUBE * curDetail.TOTAL_LENL4 / curPlan.REQR_ROLL_LEN * curPlan.REQR_ROLL_WT;
        record.REMARK = curPlan.REMARK;

        record.CUT_VALID = curDetail.CUT_VALID;
        record.A_SAW_LEN = curDetail.A_SAW_LEN;
        record.A_REMAIN_LEN = curDetail.A_REMAIN_LEN;
        record.REMAIN_VALID = curDetail.REMAIN_VALID;
        record.SP_REMAIN_LEN = curDetail.SP_REMAIN_LEN;
        record.A_SAW_NUM = curDetail.A_SAW_NUM;
        record.SP_SAW_NUM = curDetail.SP_SAW_NUM;
        record.A_REPLACE = curDetail.A_REPLACE;
        record.SP_REPLACE = curDetail.SP_REPLACE;
        record.TOTAL_NUML4 = curDetail.SIGMA_NUML4;
        record.EXTEND4 = curPlan.EXTEND4;
    }


    //统计每个轧批的收得率
    public static RateOfPlan statRecordToPlan(List<RateOfRecord> subRecordList)
    {
        RateOfPlan rp = new RateOfPlan();
        rp.MTRL_NO = subRecordList[0].MTRL_NO;
        rp.RL_NO = subRecordList[0].RL_NO;
        rp.PLAN_SEQ_NO = subRecordList[0].PLAN_SEQ_NO;
        rp.BILLET_DIA = subRecordList[0].BILLET_DIA;
        rp.BILLET_LEN = subRecordList[0].BILLET_LEN;
        rp.REQR_ROLL_TUBE = subRecordList[0].REQR_ROLL_TUBE;
        rp.REQR_ROLL_WT = subRecordList[0].REQR_ROLL_WT;
        rp.RATE = subRecordList[0].RATE;
        rp.LEN_FROM = subRecordList[0].LEN_FROM;
        rp.LEN_TO = subRecordList[0].LEN_TO;
        rp.REMARK = subRecordList[0].REMARK;
        //需统计的量。
        rp.CUT_WEIGHT = 0;
        rp.COMP_WEIGHT = 0;
        double rate = 0;
        double avg_l2 = 0;
        foreach (RateOfRecord rr in subRecordList)
        {
            rp.CUT_WEIGHT += rr.CUT_WEIGHT;
            rp.COMP_WEIGHT += rr.COMP_WEIGHT;
            rate += rr.CUT_WEIGHT * rr.ACTUAL_RATE;
            avg_l2 += rr.CUT_WEIGHT * rr.ACTUAL_AVG_L2;
        }

        rate = rate / rp.CUT_WEIGHT; //这里是统计一个轧批的记录，要么都有实际收得率要么都没有实际收得率。
        avg_l2 = avg_l2 / rp.CUT_WEIGHT;
        rp.ACTUAL_RATE = rate;
        rp.ACTUAL_AVG_L2 = avg_l2;
        rp.COMP_RATE = rate / rp.RATE;
        rp.COMP_AVG_L2 = avg_l2 / rp.BILLET_LEN;
        return rp;
    }


    //统计投料结果收得率
    public RateOfAll statAllPlan(List<RateOfPlan> RateOfPlanList)
    {
        RateOfAll ra = new RateOfAll();
        ra.REQR_ROLL_WT = 0;
        ra.CUT_WEIGHT = 0;
        double wt = 0;
        double rate = 0;
        double avg_l2 = 0;
        double comp_wt = 0;
        double comp_rate = 0;
        double comp_avg_l2 = 0;
        foreach (RateOfPlan rp in RateOfPlanList)
        {
            ra.REQR_ROLL_WT += rp.REQR_ROLL_WT;
            ra.CUT_WEIGHT += rp.CUT_WEIGHT;
            wt += (rp.ACTUAL_RATE != 0) ? rp.CUT_WEIGHT : 0; //对于没有轧批或有效计划的投料记录，不均分收得率。
            rate += rp.CUT_WEIGHT * rp.ACTUAL_RATE;
            avg_l2 += rp.CUT_WEIGHT * rp.ACTUAL_AVG_L2;
            comp_wt += rp.COMP_WEIGHT;
            comp_rate += rp.CUT_WEIGHT * rp.COMP_RATE;
            ;
            comp_avg_l2 += rp.CUT_WEIGHT * rp.COMP_AVG_L2;
        }

        rate = rate / wt;
        avg_l2 = avg_l2 / wt;
        comp_rate = comp_rate / wt;
        comp_avg_l2 = comp_avg_l2 / wt;
        ra.ACTUAL_RATE = rate;
        ra.ACTUAL_AVG_L2 = avg_l2;
        ra.COMP_RATE = comp_rate;
        ra.COMP_AVG_L2 = comp_avg_l2;
        ra.COMP_WEIGHT = comp_wt;

        return ra;
    }

    //
};

public class Plan
{
    // 计划+工序长度要求+坯长范围+可行切割子模式列表 
    //计划信息
    public int plan_i = -1;
    public string PLAN_DATE_FROM;
    public int PLAN_EXEC_SEQ_NO; //定义计划执行序号 表TPSTR05_in
    public string PLAN_SEQ_NO; //定义计划序号 表TPSTR05
    public string RL_NO; //定义轧批号 表TPSTR05
    public string MTRL_NO; //定义材质号 表TPSTR05
    public int REQR_ROLL_TUBE; //定义计划支数/需轧制根数 表TPSTR05_in
    public double REQR_ROLL_WT; //定义计划重量，表TPSTR05_in
    public double BILLET_LEN; //定义计划坯长  表TPSTR05_in
    public double BILLET_DIA; //定义轧制直径 表TPSTR05_in
    public double LEN_FROM; //定义订货长度起  
    public double LEN_TO; //定义订货长度止  

    //工序长度限制和坯长范围
    public double l2max; //定义L2管坯最大长度                  
    public double l2min; //定义L2管坯最小长度                   
    public double pmax; //定义穿孔最大长度           
    public double pmin; //定义穿孔最小长度           
    public double sizemax; //定义空减最大长度                 
    public double sizemin; //定义空减最小长度                   
    public double rollmax; //定义连轧最大长度          
    public double rollmin; //定义连轧最小长度           
    public double srmmax; //定义张减最大长度                 
    public double srmmin; //定义张减最小长度                   
    public double l3max; //定义锯切长度L3最大长度          
    public double l3min; //定义锯切长度L3最小长度
    public double billet_min; //L2管坯长度下限
    public double billet_max; //L2管坯长度下限

    //轧制信息和轧制标准
    public int roll_i = -1;
    public double REQR_ROLL_LEN; //计算，定义需轧制成品/有效长度 表TPSTR05_in
    public double REQR_STILL_LEN; //计算，最终轧制的成品/有效长度
    public double ROLL_WT;
    public int FINISHED_FLAG; //计划完成标志

    public double EXTEND1; //定义穿孔延伸系数：穿孔/坯长
    public double EXTEND2; //定义空减延伸系数
    public double EXTEND3; //定义连轧延伸系数
    public double EXTEND4; //定义张减延伸系数
    public double SP23; //张减长度-锯切有效长度
    public double VALID_LEN; //计划坯长的可用长度
    public double SAW_LEN; //计划坯长的锯切长度
    public double L4ACT; //计划坯长的辅助交货长度
    public double SP440; //计算，计算的交货长度（含余量）与起始交货长度的余量。

    public double l4max; //L4ACT最大长度      
    public double l4min; //L4ACT最小长度

    public int SAW_NUM; //计划坯长的锯切个数
    public int SAW_N; //计算，锯切长度是成品的几倍尺，计划坯长的锯切倍尺
    public double REMAIN_LEN; //计算，计划坯长的剩余长度
    public int PRONUM; //计算，计划坯长的成品根数
    public double RATE; //计划坯长的收得率
    public string REMARK; //定义备注
    public string CNAME; //定义品名分类

    //可行切割子模式List
    public List<FeasBillet1> feasBillet1List;
    public List<FeasBillet2> feasBillet2List;
};

public struct FeasBillet1
{
    public int pronum;
    public double billet_len;
    public double valid_len;

    public int saw_num;
    public int remain_saw_n;
};

public struct FeasBillet2
{
    public int pronum;
    public double billet_min;
    public double billet_max;
    public double l4_min;
    public double l4_max; //交货长度

    public int saw_num;
    public int remain_saw_n;
};

public class CutDetail
{
    public int planIdx; // 对应计划在PlanList中的索引

    public string PONO; //定义炉号 表TMMTR00
    public string STOCK_PLACE; //库位

    public string MAT_NO; //定义材料号
    public double MAT_LEN; //定义棒材长度 表TMMTR00
    public string FIX_FLAG; //定义定尺标记 表TMMTR00      0为非定尺、1为定尺、2以上倍尺
    public int MAT_TUBE; //定义可用根数
    public int MAT_CUTNUM; //定义棒材切割根数

    public double CUT_LEN; //主料长度
    public int CUT_BILLET; //主料个数
    public double REMAIN_LEN; //余料长度
    public int REMAIN_BILLET; //余料个数
    public int SAWCUT12; //单个棒材切割根数    //在切割模式中已计算完毕
    public double SP_RUBBISH; //棒材的废料

    public double CUT_VALID; //主料有效长度
    public double A_SAW_LEN; //主料锯切长度
    public int A_SAW_NUM; //主料锯切个数
    public double A_REMAIN_LEN; //主料剩余长度
    public int A_REMAIN_SAW_N; //主料剩余长度倍尺
    public double A_REPLACE; //主料带料长度
    public int A_NUML4; //一个主料能够得到的成品根数
    public double L4ACT; //L4实际值

    public double REMAIN_VALID; //余料有效长度
    public int SP_SAW_NUM; //余料锯切个数
    public double SP_REMAIN_LEN; //余料剩余长度
    public int SP_REMAIN_SAW_N; //主料剩余长度倍尺
    public double SP_REPLACE; //余料带料长度
    public int SP_NUML4; //一个余料能够得到的成品根数

    //单个棒材的指标
    public int SAWCUT23; //单个棒材总的锯切刀数
    public int SIGMA_NUML4; //单个棒材得到的成品根数
    public double TOTAL_LENL4; //单个棒材总的成品/有效长度
    public double RATE; //棒材收得率
    public double AVRL2; //坯长均值/计划坯长
};